package cn.shujuhai.qtadmin.platform.pluging.security;

import java.io.PrintWriter;

import org.jetbrains.annotations.NotNull;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * SecurityConfig
 *
 * @author dch798
 * @date 2021/08/22 18:08 //@EnableGlobalMethodSecurity(prePostEnabled = true)
 **/
@SpringBootConfiguration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String CONTENT_TYPE = "application/json; charset=UTF-8";
    final CaptchaCodeFilter captchaCodeFilter;
    final UserDetailsServiceImpl userDetailsServices;
    final MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    final MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    public SecurityConfig(UserDetailsServiceImpl userDetailsServices,
        MyAuthenticationSuccessHandler myAuthenticationSuccessHandler,
        MyAuthenticationFailureHandler myAuthenticationFailureHandler, CaptchaCodeFilter captchaCodeFilter) {
        this.userDetailsServices = userDetailsServices;
        this.myAuthenticationSuccessHandler = myAuthenticationSuccessHandler;
        this.myAuthenticationFailureHandler = myAuthenticationFailureHandler;
        this.captchaCodeFilter = captchaCodeFilter;
    }

    /**
     * 认证&授权
     *
     * @param http
     *            ：HttpSecurity
     */
    @Override
    protected void configure(@NotNull HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable();
        http.cors().and().addFilterBefore(captchaCodeFilter, UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            // 跨域
            .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
            // 角色配置
            // .antMatchers("/user/**").hasAnyRole("user", "admin")
            // .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/Captcha/**").permitAll()
            // .anyRequest().authenticated()
            .anyRequest().access("@myRBACService.hasPermission(request,authentication)")
            .and()
            // 登陆认证配置
            .formLogin().loginPage("/login").loginProcessingUrl("/user/doLogin")
            .successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenticationFailureHandler).permitAll()
            .and()
            // 注销配置
            .logout().logoutSuccessHandler((req, resp, auth) -> {
                resp.setContentType(CONTENT_TYPE);
                PrintWriter writer = resp.getWriter();
                writer.write(new ObjectMapper().writeValueAsString("注销成功"));
                writer.flush();
                writer.close();
            }).and()
            // 记住我
            .rememberMe()
            // 关闭跨站防护
            .and().csrf().disable()
            // session 创建策略
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            // 会话超时URL
            .invalidSessionUrl("/login")
            // session 固化保护功能
            .sessionFixation().migrateSession()
            // 同一个用户最大的登陆客户端的数量
            .maximumSessions(1)
            // true：已经登陆的用户不允许登陆，false：允许再次登陆，但之前登陆的账号会被踢下线
            .maxSessionsPreventsLogin(false)
            // session 过期或被踢下线的处理逻辑
            .expiredSessionStrategy(new CustomExpiredSessionStrategy());

    }

    @Override
    protected void configure(@NotNull AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServices).passwordEncoder(passwordEncoder());
    }

    /**
     * @param web:web
     *            application
     */
    @Override
    public void configure(@NotNull WebSecurity web) {
        web.ignoring().antMatchers("/lib/**", "/platform/**", "/doc/**");
    }

    /**
     * @return ：加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
